%define api.token.prefix {IR_TOKEN_}
%define parse.error verbose

%{

#include "IR-struct.h"
extern List function_list;
static void maddr(int * x,int delta){if(*x!=CONSTANT_ID) *x+=delta;}
static Operand dummy={0};
extern FILE * YYERROR_OUTPUT;

extern void yyrestart ( FILE *input_file );
extern int yylex();
extern int yyparse();
extern int yyerror(const char *msg);
%}

%union{
    uLL INT;
    char *id;
    int IR_relop_type;
    function *func;
    operation *stmt;
    Operand op;
}


/* declare tokens */

%token EOL
%token COLON
%token FUNCTION
%token LABEL
%token SHARP
%token<INT> INT
%token ASSIGN
%token PLUS
%token MINUS
%token STAR
%token DIV
%token IF
%token<IR_relop_type> RELOP
%token ADDR_OF
%token GOTO
%token RETURN
%token DEC
%token ARG
%token CALL
%token PARAM
%token READ
%token WRITE
%token<id> ID

%type ELON
%type IR_ALL
%type IR_program
%type<func> IR_function
%type<stmt> IR_stmt
%type<op> IR_val
%type<op> IR_label
%type<INT> IR_var

%%
IR_ALL      : ELON IR_program
            ;

IR_program  : IR_program IR_function                    { LIST_INSERT(function_list, $2); }
            |                                           
            ;
IR_function : FUNCTION ID COLON EOL                     { SET_V($$, function, .name=$2) }
            | IR_function PARAM IR_var EOL              { $$ = $1; LIST_INSERT($$->param,$3); }
            | IR_function IR_stmt EOL                   { $$ = $1; function_insert_statement($$, $2); }
            ;
IR_stmt     : IR_val ASSIGN IR_val                      { $$=operation_build(OP_ASSIGN,$1,$3,dummy,0);}
            | IR_val ASSIGN IR_val PLUS IR_val          { $$=operation_build(OP_ADD,$1,$3,$5,0); }
            | IR_val ASSIGN IR_val MINUS IR_val         { $$=operation_build(OP_SUB,$1,$3,$5,0); }
            | IR_val ASSIGN IR_val STAR IR_val          { $$=operation_build(OP_MUL,$1,$3,$5,0); }
            | IR_val ASSIGN IR_val DIV IR_val           { $$=operation_build(OP_DIV,$1,$3,$5,0); }
            | LABEL IR_label COLON                      { $$=operation_build(OP_LABEL,$2,dummy,dummy,0); }
            | GOTO IR_label                             { $$=operation_build(OP_GOTO,$2,dummy,dummy,0); }
            | IF IR_val RELOP IR_val GOTO IR_label      { $$=operation_build(OP_IF,$2,$4,$6,$3); }
            | RETURN IR_val                             { $$=operation_build(OP_RET,$2,dummy,dummy,0); }
            | DEC IR_val INT                            { $$=operation_build(OP_DEC,$2,dummy,dummy,$3); }
            | ARG IR_val                                { $$=operation_build(OP_ARG,$2,dummy,dummy,0); }
            | IR_val ASSIGN CALL ID                     { $$=operation_build(OP_CALL,$1,dummy,dummy,(uLL)$4);}
            | READ IR_val                               { $$=operation_build(OP_READ,$2,dummy,dummy,0); }
            | WRITE IR_val                              { $$=operation_build(OP_WRITE,$2,dummy,dummy,0); }
            ;
IR_val      : IR_var                                    { $$.var.no=$1; $$.var.addr_status=0; }
            | SHARP INT                                 { $$.var.no=$2; $$.var.addr_status=CONSTANT_ID; }
            | ADDR_OF IR_val                            { $$ = $2; maddr(&$$.var.addr_status,-1); }
            | STAR IR_val                               { $$ = $2; maddr(&$$.var.addr_status,1);}
            ;
IR_var      : ID                                        { $$ = insert_var($1); free($1); }
            ;
IR_label    : ID                                        { $$.label = insert_label($1); free($1); }
            ;
ELON        : EOL ELON
            |
            ;
%%

int IR_yyerror(const char *msg) {
    fprintf(YYERROR_OUTPUT, "IR syntax error: %s\n", msg);
    return 0;
}

//// ================================== args stack ==================================

#include "lex.yy.c"
